/**
 *
 * This class transforms the node domain data structures into relevant outputs like:
 *
 *  - a JSON list/Tree(s) for viewing the attributes of nodes on a grid on UI on a web page
 *  - JSON Timeline for viewing on a timeline
 *  - Dependency 
 *
 */
class NodeDomainTransforms
{
    NodeDomain  nodeDomain
    String      outputFolderPath
    static def logger = Logger.getLogger()

    NodeDomainTransforms(NodeDomain nodeDomain, String outputFolderPath)
    {
        this.nodeDomain = nodeDomain
        this.outputFolderPath = outputFolderPath

        logger.info("Creating output folder:"+outputFolderPath)

        // create the path if its not present
        new File(outputFolderPath).mkdirs()
    }


    /**
     * generates the graphical view
     */
    def generateGraphs()
    {
        logger.info("Generating NodeDomain graphs")

        Graphviz ggen = new Graphviz()
        ggen.newGraph(nodeDomain.name, true)

        def gNodes = [:]

        println "ZZZZZZZZZZZZZ:nodes list size:"+nodeDomain.nodeList.nodes.size()
        println nodeDomain.nodeList.nodes
        //------------------------------------------------
        // first, generate the whole Domain's graph
        nodeDomain.nodeList.nodes.each{ nodeKey, nodeObj ->
            def gNode = ggen.newGraphNode(nodeObj.name, nodeObj.name)

            // apply styles if available
            if(nodeObj.nodeStyle!=null)
            {
                if(nodeObj.nodeStyle.shape!=null)
                    ggen.setGraphNodeStyle(gNode, 
                                            nodeObj.nodeStyle.shape, 
                                            nodeObj.nodeStyle.mode, 
                                            nodeObj.nodeStyle.lineColor, 
                                            nodeObj.nodeStyle.fillColor)
            }

            gNodes.put(nodeObj.name, gNode)
        }

        // generate each graph's edge
        if(nodeDomain.nodeGraph!=null)
        {
            nodeDomain.nodeGraph.nodeEdges.each{ nodeEdge ->
                // first get it from the master list
                def gNodeA = gNodes.get(nodeEdge.nodeStartName)
                
                // if its not present in the master list, then create a new one based on hte name in the graph file and add it to master list as well
                if(gNodeA==null)
                {
                    gNodeA = ggen.newGraphNode(nodeEdge.nodeStartName, nodeEdge.nodeStartName)
                    gNodes.add(gNodeA)
                }

                def gNodeB = gNodes.get(nodeEdge.nodeEndName)

                // if its not present in the master list, then create a new one based on hte name in the graph file and add it to master list as well
                if(gNodeB==null)
                {
                    gNodeB = ggen.newGraphNode(nodeEdge.nodeStartName, nodeEdge.nodeStartName)
                    gNodes.add(gNodeB)
                }

                if(gNodeA!=null && gNodeB!=null)
                    ggen.newGraphEdge(nodeEdge.caption, gNodeA, gNodeB)
            }

            if(nodeDomain.nodeGroupList!=null)
            {
                // make each node a part of the groups they belong to
                nodeDomain.nodeGroupList.nodeGroups.each { nodeGroupName, nodeGroup ->

                    def ggroup = ggen.newGraphGroup(nodeGroup.name)

                    nodeGroup.nodeNames.each { nodeName ->
                                   def gNode = gNodes.get(nodeName)

                                   // if its not present in the master list, then create a new one based on hte name in the graph file and add it to master list as well
                                   if(gNode==null)
                                   {
                                       gNode = ggen.newGraphNode(nodeName, nodeName)
                                       gNodes.put(nodeName, gNode)
                                   }
                                   // now add it to the group
                                   if(gNode!=null)
                                        gNode.setMemberOf(ggroup)
                    }
                }
            }
        }

        def dotFilePath = ggen.genDotFileInFolder(nodeDomain.name.replace(" ", "_")+"_whole", outputFolderPath)
        logger.info("\t|--Finished generating NodeDomain graph dot file")

        ggen.genDotImageInFolder(".", dotFilePath, outputFolderPath)
        logger.info("\t|--Finished generating NodeDomain graph PNG image")

        //------------------------------------------------
        // now, generate each node's individual dependencies
        logger.info("Generating Each Node's dependencies graph dot file")

        def ctr = 1
        nodeDomain.nodeList.nodes.each { nodeKey, nodeObj ->
            ggen = new Graphviz()
            ggen.newGraph(nodeObj.name, true)
            def gNode = ggen.newGraphNode(nodeObj.name, nodeObj.name)

            if(nodeDomain.nodeGraph!=null)
            {
                nodeDomain.nodeGraph.nodeEdges.each{ nodeEdge ->
                    if(nodeEdge.nodeStartName.equals(nodeObj.name))
                    {
                        def gNodeA = gNode
                        def gNodeB = ggen.newGraphNode(nodeEdge.nodeEndName, nodeEdge.nodeEndName)

                        ggen.newGraphEdge(nodeEdge.caption, gNodeA, gNodeB)
                    }
                    else if(nodeEdge.nodeEndName.equals(nodeObj.name))
                    {
                        def gNodeA = ggen.newGraphNode(nodeEdge.nodeStartName, nodeEdge.nodeStartName)
                        def gNodeB = gNode

                        ggen.newGraphEdge(nodeEdge.caption, gNodeA, gNodeB)
                    }
                }
            }

            logger.info("\t|--" + ctr + " Generating DOT file dependency graphs for Node:"+nodeObj.name)
            dotFilePath = ggen.genDotFileInFolder(nodeObj.name.replace(" ", "_"), outputFolderPath)

            logger.info("\t|--" + ctr + " Generating PNG file dependency graphs for Node:"+nodeObj.name)
            ggen.genDotImageInFolder(".", dotFilePath, outputFolderPath)
            ctr++
        }
        
    }


    /**
     * This returns the Html for 1 NodeObj
     */
    String getNodeObjHtml(NodeObj nodeObj)
    {
        String      dotFileLink = nodeObj.name.replace(" ", "_") + ".dot"
        String      imageFileLink = dotFileLink + ".png"

        String attrTable = "<table border=1>"

        nodeObj.attrs.each { attrKey, attrValue ->
            attrTable = attrTable + "<tr><td>"+ attrKey +"</td><td>"+ attrValue +"</td></tr>"
        }

        attrTable = attrTable + "<tr><td>Start Date:</td><td>"+ Utils.getFormattedMMDDYYDate(nodeObj.eventPt.startDate) +"</td></tr>"
        if(nodeObj.eventPt.endDate!=null)
            attrTable = attrTable + "<tr><td>End Date:</td><td>"+ Utils.getFormattedMMDDYYDate(nodeObj.eventPt.endDate) +"</td></tr>"

        attrTable = attrTable + "</table>"

        String nodeObjHtmlTemplate = """
            <html>
              <head>
                <title>Object: ${nodeObj.name}</title>
                <link rel="stylesheet" type="text/css" href="styles.css">
              <head>
            	<body>

            		<div id="content">

            			<h1><b>${nodeDomain.name}</b></h1>
            			<h5>You are at:<a href="index.html">Home</a>/<a href="${nodeObj.name}.html">${nodeObj.name}</a></h5>

            			<ul>
            				<li>Use this link to embed or download(image in .png format):<a href="${imageFileLink}">${nodeObj.name}.dot.png</a></li>
            				<li>Use this link to embed or download(.dot file  that can be used to convert to SVG, PDF or other formats):<a href="${dotFileLink}">${nodeObj.name}.dot</a></li>
            				<img src="${imageFileLink}"/>
            			</ul>

            		    ${attrTable}

            	</body>
            </html>
            """

        return nodeObjHtmlTemplate
    }


    /**
     * This generates the html pages that includes the image and the attributes for each individual Node Object
     * on clicking the node object from the timeline or the list, it goes to this page
     */
    String generateNodeObjHtmls()
    {
        logger.info("Generating the Node Object Htmls for the domain:"+nodeDomain.name)

        def ctr = 1
        nodeDomain.nodeList.nodes.each{ nodeKey, nodeObj ->
            //println getNodeObjHtml(nodeObj)
            logger.info("\t|--"+ctr+". Generating "+nodeObj.name.replace(' ','_')+".html")
            def text = getNodeObjHtml(nodeObj)
            new File(new File(outputFolderPath+"/"+nodeObj.name.replace(' ', '_')+".html").getCanonicalPath()).write(text)
            ctr++
        }
    }


    /**
     * KEEPING GETS SEPARATE FROM WRITE TO FILE for serving dynamically thru a web server
     * generates the JSON js file for use by the Similie timeline widget
     */
    // gets the JSON-based format timeline data from the eventpts
    String getJsonTimeline()
    {
        def eventPts = []

        // collect all the eventPts first
        if(nodeDomain.nodeList  != null)
        {
            nodeDomain.nodeList.nodes.each { nodeKey, nodeObj ->
                eventPts.add(nodeObj.eventPt)
            }
        }

        def jsonTimeline = NodeEventPtsUtil.getEventPtsJson(eventPts)

        return jsonTimeline
    }


    // writes the supplied timeline to a file
    void writeJsonTimelineToFile(String jsonTimeline)
    {
        logger.info( "writing event Pts to JSON file")
        File file = new File(new File(outputFolderPath + "/" + nodeDomain.name.replace(" ","_")+ "_timeline.js").getCanonicalPath())
        file.write(jsonTimeline.toString())
        logger.info( "finished writing BP eventPts to JSON file:"+file.toString())
    }

    
    // this gets timeline and writes it to a file
    void generateJsonTimeline()
    {
        def nodeDomainJsonTimeline = this.getJsonTimeline()
        this.writeJsonTimelineToFile(nodeDomainJsonTimeline)
    }


    /**
     * generates the JSON list for use by the ExtJS List view
     */
    String getJSONList()
    {
        /**
         * creates and returns the JS list for the list of services
         */
        logger.info("Creating the list views for the Domain:"+nodeDomain.name)

        def row = ""
        def rows = []
        def str1 = "var list=[\n"
        def str2 = "];\n"
        def str = ""
        def ctr = 1

        nodeDomain.nodeList.nodes.each{ nodeKey, nodeObj ->
            //eventPts.add(nodeObj.eventPt)
            //row = "['" + ctr + ".', '" + nodeObj.name + "'"

            def link = nodeObj.name.replace(' ','_')

            row = "['${ctr}', '<a href=${link}.html>${nodeObj.name}</a>', '${nodeObj.type}'"

            nodeObj.attrs.each { attrKey, attrValue ->
                row = row + ",'" + attrValue + "'"
            }

            row = row + ",'" + Utils.getFormattedMMDDYYDate(nodeObj.eventPt.startDate) + "'"
            if(nodeObj.eventPt.endDate!=null)
                row = row + ",'" + Utils.getFormattedMMDDYYDate(nodeObj.eventPt.endDate) + "'"

            row = row + "]"
            str = str + row + ",\n"
            //rows.add(row)
            ctr++
        }

        // create the full string
        str = str1 + str + str2

        def strBuf = new StringBuffer(str)
        strBuf.setCharAt(str.lastIndexOf(','), ' ' as char)

        return strBuf.toString()
    }


    void writeJsonListToFile(String jsonList)
    {
        logger.info( "writing Node List Attributes to JSON file")
        File file = new File(new File(outputFolderPath + "/" + nodeDomain.name.replace(" ","_")+ "_list.js").getCanonicalPath())
        file.write(jsonList.toString())
        logger.info( "finished writing Node List Attributes to JSON file"+file.toString())
    }
    
    
    /**
     * generates the list JSON file for the Node->Attribute values List view
     */
     void generateJsonList()
     {
         logger.info("Generating the List views for the Domain:"+nodeDomain.name)

         def listStr = getJSONList()
         writeJsonListToFile(listStr)
     }


    /**
     * generates the JSON list for use by the ExtJS Tree view
     */
    String getJSONTree()
    {
    }


    /**
     * Copies the html files from the folder if they exist
     */
    boolean copyHtmlFiles()
    {
        boolean result = false
        logger.info("Copying the html files for the domain")

        // check to see if the folder is present

        if(Utils.fileExistsLocalOrRemote(nodeDomain.sourceDocsLocation + "/templates/index.html"))
        {
            result = true
            def filesList = ["index.html", "styles.css", nodeDomain.name + "_list.html", nodeDomain.name + "_timeline.html", nodeDomain.name + "_graph.html"]

            def ctr = 1
            filesList.each { fileName ->
                logger.info("\t|--"+ctr+". Copying "+fileName+ " to " + new File(this.outputFolderPath + "/" + fileName).getCanonicalPath() )

                if(Utils.fileExistsLocalOrRemote(nodeDomain.sourceDocsLocation + "/templates/" + fileName))
                    Utils.copyFileLocalOrRemote(nodeDomain.sourceDocsLocation + "/templates/" + fileName, this.outputFolderPath + "/" + fileName)
                else
                    logger.info("WARNING: Could not find file:"+nodeDomain.sourceDocsLocation + "/templates/" + fileName)

                ctr++
            }
        }
        else
        {
            result = false
            logger.info("WARNING: Could not find the html files for the domain")
        }
        
        return result
    }



    static def runTransforms(String domainName, String sourceDocsLocation, String domainGenHome)
    {
        def logLines

        try
        {
            logger.initCollection()

            //def outputFolderPath = new File(ConfigVars.DOMAIN_GEN_HOME + "/" + domainName).getCanonicalPath()
            def outputFolderPath = new File(domainGenHome + "/" + domainName).getCanonicalPath()

            NodeDomain nodeDomain = NodeDomainReader.initFromDomainFolder(domainName, sourceDocsLocation, logger)
            //NodeDomain nodeDomain = NodeDomainReader.initFromDomainFolder("MyDomain", "http://localhost:8080/timegraph/MyDomain")
            NodeDomainTransforms transforms = new NodeDomainTransforms(nodeDomain, outputFolderPath)
            logger.info("Generating Json Timeline")
            transforms.generateJsonTimeline()

            logger.info("Generating Json List")
            logger.info transforms.generateJsonList()

            logger.info("Generating Graphs")
            transforms.generateGraphs()

            logger.info("Copying Domain HTML files")
            boolean foundHtmlFiles = transforms.copyHtmlFiles()

            if(foundHtmlFiles)
            {
                logger.info("Generating NodeObj Html files")
                transforms.generateNodeObjHtmls()
            }
            else
            {
                logger.info("WARNING: did not generate the Node html files because they were not found.")
            }
        }
        catch(MyException e)
        {
            e.printStackTrace()
            logger.error("ERROR: The Master List of Nodes was not found.")
        }
        catch(e)
        {
            e.printStackTrace()
            logger.error("Unknown Exception:" + e.getMessage())
        }
        finally
        {
            logLines = logger.getCollection()
            logger.endCollection()
        }

        return logLines
    }



    static void main(args)
    {
        // ./rungroovy src/impl/NodeDomainTransforms.groovy MyDomain http://localhost:8080/
        // or ./rungroovy src/impl/NodeDomainTransforms.groovy MyDomain ./
        //NodeDomain nodeDomain = NodeDomainReader.initFromGenericExample()
        // make the folder if necessary
        // defaults
        String domainName = "MyDomain"
        String docsLocation = "../../MyDomain.source"

        println args.size()
        if(args.size() == 1)
        {
            domainName = args[0]
        }
        else if(args.size() == 2)
        {
            domainName = args[0]
            docsLocation = args[1]
        }
        else
        {
            println "Usage:... <domainName> <docsLocation>\n for e.g. groovy ./src/impl/NodeDomainTransforms.groovy MyDomain ./MyDomain"
            println "or <domainName> <docsLocation>\n for e.g. groovy ./src/impl/NodeDomainTransforms.groovy MyDomain http://localhost:8080/timegraph/MyDomain.source"
            return
        }

        //def logLines = runTransforms("MyDomain", "http://lo./calhost:8080/timegraph/MyDomain.source", ConfigVars.DOMAIN_GEN_HOME)
        def logLines = runTransforms(domainName, docsLocation, ConfigVars.DOMAIN_GEN_HOME)
        //println logLines
    }
}
